package edu.asu.acns.android;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.Map;
import java.util.Random;

import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences.Editor;
import android.content.res.Configuration;
import android.os.BatteryManager;
import android.os.Debug;
import android.os.IBinder;
import android.util.Log;

public class DeviceElasticityManager extends Service {
	// TODO change -
	public static final String CES_URL = "http://184.73.36.139/ces/ces.aspx?flag=true";
	public static final String STATS_URL = "http://184.73.36.139/cws/CalculateStatsWS.asmx/GetPatientStats";

	@Override
	public void onCreate() {
		// Register the battery receiver
		IntentFilter lowIntentFilter = new IntentFilter(
				Intent.ACTION_BATTERY_LOW);
		registerReceiver(new BatteryLowReceiver(this), lowIntentFilter);
		IntentFilter okIntentFilter = new IntentFilter(
				Intent.ACTION_BATTERY_OKAY);
		registerReceiver(new BatteryLowReceiver(this), okIntentFilter);
	}

	@Override
	public void onConfigurationChanged(Configuration newConfig) {
		super.onConfigurationChanged(newConfig);
	}

	@Override
	public void onStart(Intent intent, int startId) {
		// received intent from the activity
		Thread demThread = new Thread(new DEMTask(this, intent), "DEM-"
				+ System.currentTimeMillis());
		demThread.start();

	}

	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}

}

class BatteryLowReceiver extends BroadcastReceiver {

	private Service service;

	public BatteryLowReceiver(Service serv) {
		service = serv;
	}

	@Override
	public void onReceive(Context arg0, Intent arg1) {

		Editor e = service.getApplication().getSharedPreferences("cesfile", 1)
				.edit();
		e.putString("battery", "low");
		e.commit();

	}

}

class BatteryOkReceiver extends BroadcastReceiver {

	private Service service;

	public BatteryOkReceiver(Service serv) {
		service = serv;
	}

	@Override
	public void onReceive(Context arg0, Intent arg1) {

		Editor e = service.getApplication().getSharedPreferences("cesfile", 1)
				.edit();
		e.putString("battery", "ok");
		e.commit();

	}

}

class DEMTask implements Runnable {

	private Intent intent;
	private Service demService;
	private String function;

	public static final String PREF_FILE = "cesfile";
	public static final String DATA_FROM_CES = "outputurl";
	public final static float FLOAT_MAX = 1000000000;

	public DEMTask(Service service, Intent intent) {
		this.demService = service;
		this.intent = intent;
	}

	@Override
	public void run() {

		// this.demService.getApplication().getSharedPreferences(name, mode)
		// check if there is any data from the elastic app,else continue
		// lock this object , so no other thread updates

		while (true) {
			// update parameters
			// updateMobileDeviceResourceUsage();

			String batteryState = demService.getApplication()
					.getSharedPreferences(PREF_FILE, 1)
					.getString("battery", "");
			String resValue2 = demService.getApplication()
					.getSharedPreferences(PREF_FILE, 1).getString("RESOURCE2",
							"");
			//Debug.MemoryInfo mi = new Debug.MemoryInfo();
			
			//Debug.getMemoryInfo(mi);

			//Random rand = new Random(System.currentTimeMillis());
			//int next = rand.nextInt(100);
			long v1 = Debug.getNativeHeapSize();
			long v2 = Debug.getNativeHeapFreeSize();
			long v3 = Debug.getNativeHeapAllocatedSize();
			boolean memFull = false;
			if(v3 > .96 * v1){
				memFull = true;
			}
			
			if ("low".equals(batteryState)) {
			//if ("low".equals(batteryState) && memFull) {
				// if (next > 50) {
				// make cloudUrl available to elastic application.
				updateInfoForElasticApp("cloud");
			} else {
				// inform elastic application to use local resources for
				// computation.Use content provider
				// clean up the request from the DB
				// the below line - use "local"
				updateInfoForElasticApp("local");
			}
		}// end while
	}

	/**
	 * continuously update the memory and battery usage
	 */
	private void updateMobileDeviceResourceUsage() {
		Editor e = demService.getApplication().getSharedPreferences(PREF_FILE,
				1).edit();
		e.putString("RESOURCE1", "resource1value");
		e.putString("RESOURCE2", "resource2value");
		e.commit();
	}

	private void updateInfoForElasticApp(String data) {

		// check and batch update requests - only 5 requests at this point of
		// time
		String[] clientRequests = new String[5];
		// local computation
		if ("local".equals(data)) {
			Editor e = demService.getApplication().getSharedPreferences(
					PREF_FILE, 1).edit();

			// update outputs
			for (int i = 0; i < clientRequests.length; i++) {
				e.putString("clientrequest" + i, "processed");
			}
			e.commit();
			return;
		}// end local
		else if ("cloud".equals(data)) {
			// if cloud computation, fetch the URL
			for (int i = 0; i < clientRequests.length; i++) {
				clientRequests[i] = demService.getApplication()
						.getSharedPreferences(PREF_FILE, 1).getString(
								"clientrequest" + 0, "local");
			}

			Editor e = demService.getApplication().getSharedPreferences(
					PREF_FILE, 1).edit();

			// update outputs
			for (int i = 0; i < clientRequests.length; i++) {
				if (!"processed".equals(clientRequests[i])) {
					e.putString("outputurl" + i, fetchInformationFromCES());
					e.putString("clientrequest" + i, "processed");
				}
			}
			e.commit();
		}
	}

	/*
	 * Make decision if module to be executed on cloud or device
	 * 
	 * @return
	 */
	/*
	 * private boolean isCloudComputationRequired_old() { // examine values,
	 * memory,battery etc. and decide whether to offload to // cloud //
	 * predefined functions for the DEM to check if
	 * ("multiplication".equals(function)) {
	 * 
	 * float f1 = demService.getApplication().getSharedPreferences( PREF_FILE,
	 * 0).getFloat("float1", -1111); float f2 =
	 * demService.getApplication().getSharedPreferences( PREF_FILE,
	 * 0).getFloat("float2", -1111);
	 * 
	 * if (f1 > FLOAT_MAX || f2 > FLOAT_MAX) { return true; }
	 * 
	 * } else if ("factorial".equals(function)) { int i =
	 * demService.getApplication().getSharedPreferences(PREF_FILE,
	 * 0).getInt("int1", -1111); if (i > 50) { return true; } } else if
	 * ("power".equals(function)) { int i =
	 * demService.getApplication().getSharedPreferences(PREF_FILE,
	 * 0).getInt("int1", -1111); if (i > 50) { return true; } }
	 * 
	 * return true; }
	 */

	private String fetchInformationFromCES() {
		// return
		// "http://www.appleinsider.com/appleinsider.rss";http://207.58.150.187/appleinsider.rss
		// String data =
		// getHttpInfoFromCESService(DeviceElasticityManager.CES_URL);
		return DeviceElasticityManager.STATS_URL;
		// return
		// "http://184.73.42.208/cws/CalculateStatsWS.asmx/GetPatientStats";
	}

	private String getHttpInfoFromCESService(String URL) {
		int BUFFER_SIZE = 2000;
		InputStream in = null;
		try {
			in = openHttpConnection(URL);
		} catch (IOException e1) {
			e1.printStackTrace();
			return "";
		}

		InputStreamReader isr = new InputStreamReader(in);
		int charRead;
		String str = "";
		char[] inputBuffer = new char[BUFFER_SIZE];
		try {
			while ((charRead = isr.read(inputBuffer)) > 0) {
				// ---convert the chars to a String---
				String readString = String
						.copyValueOf(inputBuffer, 0, charRead);
				str += readString;
				inputBuffer = new char[BUFFER_SIZE];
			}
			in.close();
		} catch (IOException e) {
			e.printStackTrace();
			return "";
		}
		return str;
	}

	private InputStream openHttpConnection(String urlString) throws IOException {
		InputStream in = null;
		int response = -1;

		URL url = new URL(urlString);
		URLConnection conn = url.openConnection();

		if (!(conn instanceof HttpURLConnection))
			throw new IOException("Not an HTTP connection");

		try {
			HttpURLConnection httpConn = (HttpURLConnection) conn;
			httpConn.setAllowUserInteraction(false);
			httpConn.setInstanceFollowRedirects(true);
			httpConn.setRequestMethod("GET");
			httpConn.connect();

			response = httpConn.getResponseCode();
			if (response == HttpURLConnection.HTTP_OK) {
				in = httpConn.getInputStream();
			}
		} catch (Exception ex) {
			throw new IOException("Error connecting");
		}
		return in;
	}

}
